using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using T.FAS.Runtime.Base.Entity;
using T.FAS.Runtime.Metadata.Entity.ScheduleJob;
using T.FAS.Runtime.ScheduleJob.Engine;
using T.FAS.Runtime.ScheduleJob.Manager;
using T.FAS.Runtime.ScheduleJob.SPI;
using T.ServiceComponent.Cache.Service;

namespace T.FAS.Runtime.ScheduleJob.Service
{
    public class ScheduleJobGlobalService
    {
        private static readonly object lockObj = new object();

        private static ScheduleJobGlobalService _instance = null;
        private static IScheduleJobService sJobService = null;
        private static ScheduleJobInstanceManager instanceMgr = null;
        private static MetadataRTPService metadataService = null;

        private static CacheService cacheService = null;
        private string ReadyToFireJob = "FAS_ScheduleJob_ReadyToFire";

        private int CheckInterval = 60000;
        private static Dictionary<string, Metadata.Entity.ScheduleJob.ScheduleJob> scheduleJobStores = null;
        private static Dictionary<string, ScheduleJobInstanceStore> instanceStores = null;
        //取消Token
        private CancellationTokenSource cts;
        public static ScheduleJobGlobalService GetInstance()
        {
            if (_instance == null)
            {
                lock (lockObj)
                {
                    if (_instance == null)
                        _instance = new ScheduleJobGlobalService();
                }
            }
            return _instance;
        }
        private ScheduleJobGlobalService()
        {
            instanceMgr = ScheduleJobInstanceManager.GetInstance();
            metadataService = MetadataRTPService.GetInstance();
            scheduleJobStores = new Dictionary<string, Metadata.Entity.ScheduleJob.ScheduleJob>();
            instanceStores = new Dictionary<string, ScheduleJobInstanceStore>();
            sJobService = ScheduleJobService.GetInstance();
            cacheService = CacheService.GetInstance("DefaultPool");
        }
        public void Start()
        {
            var checkTask = new Task(()=>Check(),cts.Token,TaskCreationOptions.LongRunning);
            checkTask.ContinueWith(CheckTaskWatcher);
            checkTask.Start();
        }
        private void CheckTaskWatcher(Task task)
        {
            if (task.IsFaulted)
            {
                LocalErrorLogService.Write(task.Exception);
            }
            else if (task.IsCompleted)
            {
                var ex = new System.Exception("计划任务元数据自启动线程执行完成!");
                LocalErrorLogService.Write(ex);
            }
            if (cts.IsCancellationRequested == false)
                Start();
        }
        private void Check()
        {
            while (cts.IsCancellationRequested == false)
            {
                scheduleJobStores.Clear();
                instanceStores.Clear();
                GetReadyToFireJob();
                Thread.Sleep(CheckInterval);
            }
        }

        private void GetReadyToFireJob()
        {
            var storeList = instanceMgr.GetReadyToFireScheduleJobInstances();
            var errMsg = new StringBuilder();
            if (storeList != null && storeList.Count > 0)
            {
                storeList.ForEach(s =>
                {
                    var jobMetadata = metadataService.GetMetadata(s.ScheduleJobID) as Metadata.Entity.ScheduleJob.ScheduleJob;
                    if (jobMetadata != null)
                    {
                        if (!jobMetadata.IsDisabled)
                        {
                            //UpdateLocalCache(jobMetadata, s);
                            var canInitJob = TryLockJob(jobMetadata);
                            if (canInitJob)
                            {
                                try
                                {
                                    FireJob(jobMetadata);
                                }
                                catch (Exception e)
                                {
                                    errMsg.AppendLine(e.ToString());
                                }
                            }
                        }
                    }
                });
            }
            if (errMsg.Length > 0)
            {
                throw new Exception(errMsg.ToString());
            }
        }
        private void UpdateLocalCache(Metadata.Entity.ScheduleJob.ScheduleJob sJob, ScheduleJobInstanceStore store)
        {
            scheduleJobStores[sJob.ID] = sJob;
            instanceStores[store.ScheduleJobID] = store;
        }

        private void FireJob(Metadata.Entity.ScheduleJob.ScheduleJob sJob)
        {
            try
            {
                var instance = sJobService.CreateScheduleJobInstance(sJob.ID);
                sJobService.StartJob(instance);
            }
            catch (Exception ex)
            {
                throw new Exception(string.Format(CommonStringClassService.ScheduleJobGlobalService_FireJob,sJob.ID,sJob.Code,ex.ToString()));
            }
            finally
            {
                ReleaseJobLock(sJob);
            }
        }

        /// <summary>
        /// 尝试锁定某一个计划任务进行初始化启动
        /// </summary>
        /// <param name="sJob"></param>
        /// <returns></returns>
        private bool TryLockJob(Metadata.Entity.ScheduleJob.ScheduleJob sJob)
        {
            if (sJob == null)
                return false;

            using (T.ServiceComponent.Cache.Service.ICacheClient client = cacheService.GetClient())
            {
                var jobId = client.GetValueFromHash(sJob.ID,ReadyToFireJob);
                //如果缓存中的jobId为空,或者和当前的计划任务元数据ID不同,则删除缓存,重新加一个新的,并锁定
                if (string.IsNullOrEmpty(jobId) || !jobId.Equals(sJob.ID))
                {
                    client.RemoveEntryFromHash(sJob.ID, ReadyToFireJob);
                    client.SetEntryInHash(sJob.ID, ReadyToFireJob, sJob.ID);
                    return true;
                }
                //已经被其他进程锁定,不需要处理了
                else
                    return false;
            }
        }
        private void ReleaseJobLock(Metadata.Entity.ScheduleJob.ScheduleJob sJob)
        {
            if (sJob == null)
                return;
            using (T.ServiceComponent.Cache.Service.ICacheClient client = cacheService.GetClient())
            {
                client.RemoveEntryFromHash(sJob.ID, ReadyToFireJob);
            }
        }
    }
}
